-- -*- coding: utf-8 -*-
--
-- vim: ts=4:sts=4:sw=4:fenc=utf-8:et:
--
-- librime-lua: Extending RIME with Lua scripts
--              (https://github.com/hchunhui/librime-lua)
--
-- Lua 5.4 Reference Manual: https://www.lua.org/manual/5.4/
--
-- Usage:
--  engine:
--    ...
--    translators:
--      ...
--      - lua_translator@time_translate
--      ...
--

---
---支持 Rime 显示日期/时间/星期
---@param input string 输入码
---@param seg any 输入码信息
---@param env any 环境变量
function _G.time_translate(input, seg, env)
    --Candidate(type, start, end, text, comment)
    if (input == 'date') then
        local date = os.date('%Y-%m-%d')
        local week_cn = _G.get_week_cn()
        local chi_cal_date = _G.get_chinese_calendar_date(os.date('%Y'), os.date('%m'), os.date('%d'))
        yield(Candidate(input, seg.start, seg._end, date, '~日期'))
        yield(Candidate(input, seg.start, seg._end, date .. week_cn, '+星期'))
        yield(Candidate(input, seg.start, seg._end, chi_cal_date, '~农历'))
    elseif (input == 'time') then
        yield(Candidate(input, seg.start, seg._end, os.date('%Y-%m-%d %H:%M:%S'), ''))
        yield(Candidate(input, seg.start, seg._end, os.date('%Y-%m-%dT%H:%M:%S%z'), ''))
        yield(Candidate(input, seg.start, seg._end, os.date('%a %b %d %X %Y'), ''))
    elseif (input == 'week') then
        yield(Candidate(input, seg.start, seg._end, os.date('%A'), ''))
        yield(Candidate(input, seg.start, seg._end, os.date('%a'), ''))
        yield(Candidate(input, seg.start, seg._end, _G.tonumber(os.date('%W')) + 1, '第~周'))
    end
end

---阳历月份天数
local MONTH_DAYS = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
---[农历数据表（1901-2100）](https://www.cnblogs.com/liyang31tg/p/4123171.html)
---
---农历数据表使用32位整数的0~23位来表示农历年的闰月、春节和月份大小的信息，结构如下：
---```text
---23 22 21 20
---表示当年的闰月月份，值为0则表示无闰月
---
---19 18 17 16 15 14 13 12 11 10  9  8  7
---1  2  3  4  5  6  7  8  9  10 11 12 13
---农历正月~腊月（含闰月）大小，为0表示月小（29天），为1表示月大（30天）
---
---6 5                4 3 2 1 0
---表示春节的公历月份 表示春节的公历日期（正月初一对应的公历日期）
---```
local CHINESE_CALENDAR_DATA = {
    0x04AE53, 0x0A5748, 0x5526BD, 0x0D2650, 0x0D9544, 0x46AAB9, 0x056A4D, 0x09AD42, 0x24AEB6, 0x04AE4A, -- 1901-1910
    0x6A4DBE, 0x0A4D52, 0x0D2546, 0x5D52BA, 0x0B544E, 0x0D6A43, 0x296D37, 0x095B4B, 0x749BC1, 0x049754, -- 1911-1920
    0x0A4B48, 0x5B25BC, 0x06A550, 0x06D445, 0x4ADAB8, 0x02B64D, 0x095742, 0x2497B7, 0x04974A, 0x664B3E, -- 1921-1930
    0x0D4A51, 0x0EA546, 0x56D4BA, 0x05AD4E, 0x02B644, 0x393738, 0x092E4B, 0x7C96BF, 0x0C9553, 0x0D4A48, -- 1931-1940
    0x6DA53B, 0x0B554F, 0x056A45, 0x4AADB9, 0x025D4D, 0x092D42, 0x2C95B6, 0x0A954A, 0x7B4ABD, 0x06CA51, -- 1941-1950
    0x0B5546, 0x555ABB, 0x04DA4E, 0x0A5B43, 0x352BB8, 0x052B4C, 0x8A953F, 0x0E9552, 0x06AA48, 0x6AD53C, -- 1951-1960
    0x0AB54F, 0x04B645, 0x4A5739, 0x0A574D, 0x052642, 0x3E9335, 0x0D9549, 0x75AABE, 0x056A51, 0x096D46, -- 1961-1970
    0x54AEBB, 0x04AD4F, 0x0A4D43, 0x4D26B7, 0x0D254B, 0x8D52BF, 0x0B5452, 0x0B6A47, 0x696D3C, 0x095B50, -- 1971-1980
    0x049B45, 0x4A4BB9, 0x0A4B4D, 0xAB25C2, 0x06A554, 0x06D449, 0x6ADA3D, 0x0AB651, 0x093746, 0x5497BB, -- 1981-1990
    0x04974F, 0x064B44, 0x36A537, 0x0EA54A, 0x86B2BF, 0x05AC53, 0x0AB647, 0x5936BC, 0x092E50, 0x0C9645, -- 1991-2000
    0x4D4AB8, 0x0D4A4C, 0x0DA541, 0x25AAB6, 0x056A49, 0x7AADBD, 0x025D52, 0x092D47, 0x5C95BA, 0x0A954E, -- 2001-2010
    0x0B4A43, 0x4B5537, 0x0AD54A, 0x955ABF, 0x04BA53, 0x0A5B48, 0x652BBC, 0x052B50, 0x0A9345, 0x474AB9, -- 2011-2020
    0x06AA4C, 0x0AD541, 0x24DAB6, 0x04B64A, 0x6A573D, 0x0A4E51, 0x0D2646, 0x5E933A, 0x0D534D, 0x05AA43, -- 2021-2030
    0x36B537, 0x096D4B, 0xB4AEBF, 0x04AD53, 0x0A4D48, 0x6D25BC, 0x0D254F, 0x0D5244, 0x5DAA38, 0x0B5A4C, -- 2031-2040
    0x056D41, 0x24ADB6, 0x049B4A, 0x7A4BBE, 0x0A4B51, 0x0AA546, 0x5B52BA, 0x06D24E, 0x0ADA42, 0x355B37, -- 2041-2050
    0x09374B, 0x8497C1, 0x049753, 0x064B48, 0x66A53C, 0x0EA54F, 0x06B244, 0x4AB638, 0x0AAE4C, 0x092E42, -- 2051-2060
    0x3C9735, 0x0C9649, 0x7D4ABD, 0x0D4A51, 0x0DA545, 0x55AABA, 0x056A4E, 0x0A6D43, 0x452EB7, 0x052D4B, -- 2061-2070
    0x8A95BF, 0x0A9553, 0x0B4A47, 0x6B553B, 0x0AD54F, 0x055A45, 0x4A5D38, 0x0A5B4C, 0x052B42, 0x3A93B6, -- 2071-2080
    0x069349, 0x7729BD, 0x06AA51, 0x0AD546, 0x54DABA, 0x04B64E, 0x0A5743, 0x452738, 0x0D264A, 0x8E933E, -- 2081-2090
    0x0D5252, 0x0DAA47, 0x66B53B, 0x056D4F, 0x04AE45, 0x4A4EB9, 0x0A4D4C, 0x0D1541, 0x2D92B5, 0x0D5249, -- 2091-2100
}

---
---判断指定公历年是否为闰年
---@param year number 公历年，如 `2022`
---@return boolean 如果指定公历年为闰年，返回`true`，否则，返回`false`
function _G.is_leap_year(year)
    return (year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0 and year % 3200 ~= 0) or year % 172800 == 0
end

---
---得到指定公历年的天数
---@param year number 公历年，如 `2022`
---@return number 如果指定公历年为闰年，返回`366`，否则，返回`365`
function _G.get_year_length(year)
    if _G.is_leap_year(year) then
        return 366
    else
        return 365
    end
end

---
---得到指定公历年、月的天数
---@param year number 公历年，如 `2022`
---@param month number 指定年的月份，如 `5`
---@return number 指定公历年、月的天数
function _G.get_month_length(year, month)
    if month == 2 then
        if _G.is_leap_year(year) then
            return 29
        else
            return 28
        end
    else
        return MONTH_DAYS[month]
    end
end

---
---计算指定日期位于当年中的第几天，从1到365，或闰年366（阳历年）
---@param year number 公历年，如 `2022`
---@param month number 公历月份，如 `5`
---@param day number 公历月份的第几天，如 `19`
---@return number 指定日期位于当年中的第几天
function _G.get_day_of_year(year, month, day)
    local day_of_year = 0
    for i = 1, month - 1 do
        local month_days = _G.get_month_length(year, i)
        day_of_year = day_of_year + month_days
    end
    return day_of_year + day
end

---
---获取指定公历年的农历数据
---@param year number 公历年，如 `2022`
---@return number 指定公历年的农历数据，支持的公历年为`[1901 - 2100]`
function _G.get_chinese_calendar_data(year)
    local year_idx = year - 1901 + 1 -- Lua数组下标从 1 开始，字符串索引亦然
    return CHINESE_CALENDAR_DATA[year_idx]
end

---
---获取农历年与农历日期到春节的天数
---@param year number 公历年，如 `2022`
---@param month number 公历月份，如 `5`
---@param day number 公历月份的第几天，如 `19`
---@return number,number,number 农历数据、公历年对应的农历年、农历日期到春节的天数
function _G.calc_chinese_calendar_year_days(year, month, day)
    local chi_cal_data = _G.get_chinese_calendar_data(year)
    -- 获取农历春节（正月初一）的日期
    local chi_new_year_month = (chi_cal_data & 0x60) >> 5
    local chi_new_year_day = (chi_cal_data & 0x1F)

    local chi_year = year
    local chi_year_days

    if (chi_new_year_month > month) or ((chi_new_year_month == month) and (chi_new_year_day > day)) then
        -- 阳历日在春节前
        chi_year = chi_year - 1
        chi_cal_data = _G.get_chinese_calendar_data(chi_year)
        chi_new_year_month = (chi_cal_data & 0x60) >> 5
        chi_new_year_day = (chi_cal_data & 0x1F)
        chi_year_days = _G.get_year_length(chi_year)
            + _G.get_day_of_year(year, month, day)
            - _G.get_day_of_year(chi_year, chi_new_year_month, chi_new_year_day)
    else
        chi_year_days = _G.get_day_of_year(year, month, day)
            - _G.get_day_of_year(chi_year, chi_new_year_month, chi_new_year_day)
    end
    chi_year_days = chi_year_days + 1 -- Add today
    return chi_cal_data, chi_year, chi_year_days
end

---
---将公历日期转换为农历日期（使用数字表示），最后一位为 true，表示闰月
---
---[参考](https://blog.csdn.net/hejp_123/article/details/87278382)
---@param year number 公历年，如 `2022`
---@param month number 公历月份，如 `5`
---@param day number 公历月份的第几天，如 `19`
---@return number,number,number,boolean 公历日期对应的农历日期，最后一位为true，表示闰月：`2022`, `4`, `19`, `false`
function _G.calc_chinese_calendar_fields(year, month, day)
    -- 农历数据、农历年、农历日到春节的天数
    local chi_cal_data, chi_year, chi_year_days = _G.calc_chinese_calendar_year_days(year, month, day)
    local chi_leap_month = (chi_cal_data >> 20) & 0xF

    local bit = 19
    local chi_month = 1
    local chi_day = 1
    local month_past_days = 0
    for m = 1, 14 - 1 do
        local chi_month_leap = (chi_cal_data >> bit) & 0x1
        if month_past_days + 29 + chi_month_leap < chi_year_days then
            m = m + 1
            chi_month = m
            month_past_days = month_past_days + 29 + chi_month_leap
        else
            chi_day = chi_year_days - month_past_days
            break
        end
        bit = bit - 1
    end

    if chi_leap_month > 0 then
        -- 农历闰月调整
        if chi_leap_month == chi_month - 1 then
            chi_month = chi_month - 1
            chi_month = chi_month + 100 -- 双月
        elseif chi_leap_month < chi_month - 1 then
            chi_month = chi_month - 1
        end
    end

    if chi_month > 100 then
        chi_month = chi_month % 100
        return chi_year, chi_month, chi_day, true
    else
        return chi_year, chi_month, chi_day, false
    end
end

---天干
local HEAVENLY_STEMS = { '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸' }
---地支
local EARTHLY_BRANCHES = { '子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥' }

---属相（生肖）名称
local CHI_ZODIACS = { '鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪' }

---农历月份名（冬月与腊月为别名）
local MONTH_NAMES = { '正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊' }
---农历日期名
local DAY_NAMES = {
    '初一', '初二', '初三', '初四', '初五',
    '初六', '初七', '初八', '初九', '初十',
    '十一', '十二', '十三', '十四', '十五',
    '十六', '十七', '十八', '十九', '二十',
    '廿一', '廿二', '廿三', '廿四', '廿五',
    '廿六', '廿七', '廿八', '廿九', '三十'
}

---
---获取农历日期，样式：庚子年【鼠年】二月三十、庚子年【鼠年】闰四月廿五
---@param year string 公历年，如 `2022`
---@param month string 公历月份，如 `05`
---@param day string 公历月份的第几天，如 `19`
---@return string 农历日期，如 `壬寅年【虎年】四月十九`
function _G.get_chinese_calendar_date(year, month, day)
    local year0 = _G.tonumber(year)
    if year0 < 1901 or year0 > 2100 then
        return year .. '-' .. month .. '-' .. day
    end

    local month0 = _G.tonumber(month)
    local day0 = _G.tonumber(day)

    local chi_year, chi_month, chi_day, is_leap_month = _G.calc_chinese_calendar_fields(year0, month0, day0)
    --_G.print('农历', chi_year, chi_month, chi_day, is_leap_month)

    -- 农历年份减3（说明：补减 1）
    local chi_year_o4 = chi_year - 3 - 1
    local tg = chi_year_o4 % 10 + 1 -- 模 10，得到天干数（从1开始）
    local dz = chi_year_o4 % 12 + 1 -- 模 12，得到地支数（从1开始）

    local sexagenary_cycle = HEAVENLY_STEMS[tg] .. EARTHLY_BRANCHES[dz]
    local chi_zodiac = CHI_ZODIACS[dz]

    local month_name = MONTH_NAMES[chi_month]
    local day_name = DAY_NAMES[chi_day]

    if is_leap_month then
        month_name = '闰' .. month_name
    end
    return string.format('%s年【%s年】%s月%s', sexagenary_cycle, chi_zodiac, month_name, day_name)
end

local WEEK_CN = { '日', '一', '二', '三', '四', '五', '六' }

---
---获取汉字星期，样式：（周一）、（周四）
---@return string 汉字星期，如 `（周四）`
function _G.get_week_cn()
    -- os.date('%w')：一星期中的第几天，[0 - 6 = 星期日 - 星期六]
    -- os.date('%W')：一年中的第几个星期，0 - 52

    local week = os.date('%w')
    local week_no = _G.tonumber(week) + 1
    local week_cn = WEEK_CN[week_no]

    return '（周' .. week_cn .. '）'
end

--
--Usage: lua54.exe \Path\to\rime.lua
--_G.print(_G.get_chinese_calendar_date('2020', '01', '24'))  -- 己亥年【猪年】腊月三十
--_G.print(_G.get_chinese_calendar_date('2020', '01', '25'))  -- 庚子年【鼠年】正月初一
--_G.print(_G.get_chinese_calendar_date('2020', '06', '01'))  -- 庚子年【鼠年】闰四月初十
--_G.print(_G.get_chinese_calendar_date('2022', '05', '19'))  -- 壬寅年【虎年】四月十九
--_G.print(_G.get_chinese_calendar_date('2025', '04', '27'))  -- 乙巳年【蛇年】三月三十
--_G.print(_G.get_chinese_calendar_date('2025', '04', '28'))  -- 乙巳年【蛇年】四月初一
--_G.print(_G.get_chinese_calendar_date('2025', '07', '25'))  -- 乙巳年【蛇年】闰六月初一
